﻿using System.Collections.Generic;

namespace System {

    public interface IReadOnlyStringData : IReadOnlyData<string> { }
    public interface IValueChangedStringData : IReadOnlyStringData, IValueChangedData<string> { }
    public interface IStringDataNotifier : IValueChangedStringData, IDataNotifier<string> { }
    public interface IStringData : IStringDataNotifier, IData<string> { }

    public interface IReadOnlyBoolData : IReadOnlyData<bool> { }
    public interface IValueChangedBoolData : IReadOnlyBoolData, IValueChangedData<bool> { }
    public interface IBoolDataNotifier : IValueChangedBoolData, IDataNotifier<bool> { }
    public interface IBoolData : IBoolDataNotifier, IData<bool> { }

    public interface IReadOnlyByteData : IReadOnlyData<byte> { }
    public interface IValueChangedByteData : IReadOnlyByteData, IValueChangedData<byte> { }
    public interface IByteDataNotifier : IValueChangedByteData, IDataNotifier<byte> { }
    public interface IByteData : IByteDataNotifier, IData<byte> { }

    public interface IReadOnlySByteData : IReadOnlyData<sbyte> { }
    public interface IValueChangedSByteData : IReadOnlySByteData, IValueChangedData<sbyte> { }
    public interface ISByteDataNotifier : IValueChangedSByteData, IDataNotifier<sbyte> { }
    public interface ISByteData : ISByteDataNotifier, IData<sbyte> { }

    public interface IReadOnlyShortData : IReadOnlyData<short> { }
    public interface IValueChangedShortData : IReadOnlyShortData, IValueChangedData<short> { }
    public interface IShortDataNotifier : IValueChangedShortData, IDataNotifier<short> { }
    public interface IShortData : IShortDataNotifier, IData<short> { }

    public interface IReadOnlyUShortData : IReadOnlyData<ushort> { }
    public interface IValueChangedUShortData : IReadOnlyUShortData, IValueChangedData<ushort> { }
    public interface IUShortDataNotifier : IValueChangedUShortData, IDataNotifier<ushort> { }
    public interface IUShortData : IUShortDataNotifier, IData<ushort> { }

    public interface IReadOnlyIntData : IReadOnlyData<int> { }
    public interface IValueChangedIntData : IReadOnlyIntData, IValueChangedData<int> { }
    public interface IIntDataNotifier : IValueChangedIntData, IDataNotifier<int> { }
    public interface IIntData : IIntDataNotifier, IData<int> { }

    public interface IReadOnlyUIntData : IReadOnlyData<uint> { }
    public interface IValueChangedUIntData : IReadOnlyUIntData, IValueChangedData<uint> { }
    public interface IUIntDataNotifier : IValueChangedUIntData, IDataNotifier<uint> { }
    public interface IUIntData : IUIntDataNotifier, IData<uint> { }

    public interface IReadOnlyLongData : IReadOnlyData<long> { }
    public interface IValueChangedLongData : IReadOnlyLongData, IValueChangedData<long> { }
    public interface ILongDataNotifier : IValueChangedLongData, IDataNotifier<long> { }
    public interface ILongData : ILongDataNotifier, IData<long> { }

    public interface IReadOnlyULongData : IReadOnlyData<ulong> { }
    public interface IValueChangedULongData : IReadOnlyULongData, IValueChangedData<ulong> { }
    public interface IULongDataNotifier : IValueChangedULongData, IDataNotifier<ulong> { }
    public interface IULongData : IULongDataNotifier, IData<ulong> { }

    public interface IReadOnlyFloatData : IReadOnlyData<float> { }
    public interface IValueChangedFloatData : IReadOnlyFloatData, IValueChangedData<float> { }
    public interface IFloatDataNotifier : IValueChangedFloatData, IDataNotifier<float> { }
    public interface IFloatData : IFloatDataNotifier, IData<float> { }

    public interface IReadOnlyDoubleData : IReadOnlyData<double> { }
    public interface IValueChangedDoubleData : IReadOnlyDoubleData, IValueChangedData<double> { }
    public interface IDoubleDataNotifier : IValueChangedDoubleData, IDataNotifier<double> { }
    public interface IDoubleData : IDoubleDataNotifier, IData<double> { }

    public interface IReadOnlyDateTimeData : IReadOnlyData<DateTime> { }
    public interface IValueChangedDateTimeData : IReadOnlyDateTimeData, IValueChangedData<DateTime> { }
    public interface IDateTimeDataNotifier : IValueChangedDateTimeData, IDataNotifier<DateTime> { }
    public interface IDateTimeData : IDateTimeDataNotifier, IData<DateTime> { }

    public interface IReadOnlyTimeSpanData : IReadOnlyData<TimeSpan> { }
    public interface IValueChangedTimeSpanData : IReadOnlyTimeSpanData, IValueChangedData<TimeSpan> { }
    public interface ITimeSpanDataNotifier : IValueChangedTimeSpanData, IDataNotifier<TimeSpan> { }
    public interface ITimeSpanData : ITimeSpanDataNotifier, IData<TimeSpan> { }

    public interface IReadOnlyData<T> : IChangable {

        T Value { get; }

        IData<T> AsReadWrite();
    }

    public interface IValueChangedData<T> : IReadOnlyData<T> {

        public delegate void ValueChangedDelegate(T old, T @new);

        event ValueChangedDelegate ValueChanged;
    }

    public interface IDataNotifier<T> : IValueChangedData<T>, IChangable {

        void NotifyChangedEvent();

        void NotifyValueChangedEvent();
    }

    public interface IData<T> : IDataNotifier<T> {

        new T Value { get; set; }

        void SetValueWithOutEvent(T value);
    }

    [Implement(typeof(IReadOnlyBoolData))]
    [Implement(typeof(IValueChangedBoolData))]
    [Implement(typeof(IBoolDataNotifier))]
    [Implement(typeof(IBoolData))]
    [Implement(typeof(IReadOnlyData<bool>))]
    [Implement(typeof(IValueChangedData<bool>))]
    [Implement(typeof(IDataNotifier<bool>))]
    [Implement(typeof(IData<bool>))]
    internal sealed class KHBD_BoolData : KHBD_Data<bool>, IBoolData {
    }

    [Implement(typeof(IReadOnlyByteData))]
    [Implement(typeof(IValueChangedByteData))]
    [Implement(typeof(IByteDataNotifier))]
    [Implement(typeof(IByteData))]
    [Implement(typeof(IReadOnlyData<byte>))]
    [Implement(typeof(IValueChangedData<byte>))]
    [Implement(typeof(IDataNotifier<byte>))]
    [Implement(typeof(IData<byte>))]
    internal sealed class KHBD_ByteData : KHBD_Data<byte>, IByteData {
    }

    [Implement(typeof(IReadOnlySByteData))]
    [Implement(typeof(IValueChangedSByteData))]
    [Implement(typeof(ISByteDataNotifier))]
    [Implement(typeof(ISByteData))]
    [Implement(typeof(IReadOnlyData<sbyte>))]
    [Implement(typeof(IValueChangedData<sbyte>))]
    [Implement(typeof(IDataNotifier<sbyte>))]
    [Implement(typeof(IData<sbyte>))]
    internal sealed class KHBD_SByteData : KHBD_Data<sbyte>, ISByteData {
    }

    [Implement(typeof(IReadOnlyShortData))]
    [Implement(typeof(IValueChangedShortData))]
    [Implement(typeof(IShortDataNotifier))]
    [Implement(typeof(IShortData))]
    [Implement(typeof(IReadOnlyData<short>))]
    [Implement(typeof(IValueChangedData<short>))]
    [Implement(typeof(IDataNotifier<short>))]
    [Implement(typeof(IData<short>))]
    internal sealed class KHBD_ShortData : KHBD_Data<short>, IShortData {
    }

    [Implement(typeof(IReadOnlyUShortData))]
    [Implement(typeof(IValueChangedUShortData))]
    [Implement(typeof(IUShortDataNotifier))]
    [Implement(typeof(IUShortData))]
    [Implement(typeof(IReadOnlyData<ushort>))]
    [Implement(typeof(IValueChangedData<ushort>))]
    [Implement(typeof(IDataNotifier<ushort>))]
    [Implement(typeof(IData<ushort>))]
    internal sealed class KHBD_UShortData : KHBD_Data<ushort>, IUShortData {
    }

    [Implement(typeof(IReadOnlyIntData))]
    [Implement(typeof(IValueChangedIntData))]
    [Implement(typeof(IIntDataNotifier))]
    [Implement(typeof(IIntData))]
    [Implement(typeof(IReadOnlyData<int>))]
    [Implement(typeof(IValueChangedData<int>))]
    [Implement(typeof(IDataNotifier<int>))]
    [Implement(typeof(IData<int>))]
    internal sealed class KHBD_IntData : KHBD_Data<int>, IIntData {
    }

    [Implement(typeof(IReadOnlyUIntData))]
    [Implement(typeof(IValueChangedUIntData))]
    [Implement(typeof(IUIntDataNotifier))]
    [Implement(typeof(IUIntData))]
    [Implement(typeof(IReadOnlyData<uint>))]
    [Implement(typeof(IValueChangedData<uint>))]
    [Implement(typeof(IDataNotifier<uint>))]
    [Implement(typeof(IData<uint>))]
    internal sealed class KHBD_UIntData : KHBD_Data<uint>, IUIntData {
    }

    [Implement(typeof(IReadOnlyLongData))]
    [Implement(typeof(IValueChangedLongData))]
    [Implement(typeof(ILongDataNotifier))]
    [Implement(typeof(ILongData))]
    [Implement(typeof(IReadOnlyData<long>))]
    [Implement(typeof(IValueChangedData<long>))]
    [Implement(typeof(IDataNotifier<long>))]
    [Implement(typeof(IData<long>))]
    internal sealed class KHBD_LongData : KHBD_Data<long>, ILongData {
    }

    [Implement(typeof(IReadOnlyULongData))]
    [Implement(typeof(IValueChangedULongData))]
    [Implement(typeof(IULongDataNotifier))]
    [Implement(typeof(IULongData))]
    [Implement(typeof(IReadOnlyData<ulong>))]
    [Implement(typeof(IValueChangedData<ulong>))]
    [Implement(typeof(IDataNotifier<ulong>))]
    [Implement(typeof(IData<ulong>))]
    internal sealed class KHBD_ULongData : KHBD_Data<ulong>, IULongData {
    }

    [Implement(typeof(IReadOnlyFloatData))]
    [Implement(typeof(IValueChangedFloatData))]
    [Implement(typeof(IFloatDataNotifier))]
    [Implement(typeof(IFloatData))]
    [Implement(typeof(IReadOnlyData<float>))]
    [Implement(typeof(IValueChangedData<float>))]
    [Implement(typeof(IDataNotifier<float>))]
    [Implement(typeof(IData<float>))]
    internal sealed class KHBD_FloatData : KHBD_Data<float>, IFloatData {
    }

    [Implement(typeof(IReadOnlyDoubleData))]
    [Implement(typeof(IValueChangedDoubleData))]
    [Implement(typeof(IDoubleDataNotifier))]
    [Implement(typeof(IDoubleData))]
    [Implement(typeof(IReadOnlyData<double>))]
    [Implement(typeof(IValueChangedData<double>))]
    [Implement(typeof(IDataNotifier<double>))]
    [Implement(typeof(IData<double>))]
    internal sealed class KHBD_DoubleData : KHBD_Data<double>, IDoubleData {
    }

    [Implement(typeof(IReadOnlyDateTimeData))]
    [Implement(typeof(IValueChangedDateTimeData))]
    [Implement(typeof(IDateTimeDataNotifier))]
    [Implement(typeof(IDateTimeData))]
    [Implement(typeof(IReadOnlyData<DateTime>))]
    [Implement(typeof(IValueChangedData<DateTime>))]
    [Implement(typeof(IDataNotifier<DateTime>))]
    [Implement(typeof(IData<DateTime>))]
    internal sealed class KHBD_DateTimeData : KHBD_Data<DateTime>, IDateTimeData {
    }

    [Implement(typeof(IReadOnlyTimeSpanData))]
    [Implement(typeof(IValueChangedTimeSpanData))]
    [Implement(typeof(ITimeSpanDataNotifier))]
    [Implement(typeof(ITimeSpanData))]
    [Implement(typeof(IReadOnlyData<TimeSpan>))]
    [Implement(typeof(IValueChangedData<TimeSpan>))]
    [Implement(typeof(IDataNotifier<TimeSpan>))]
    [Implement(typeof(IData<TimeSpan>))]
    internal sealed class KHBD_TimeSpanData : KHBD_Data<TimeSpan>, ITimeSpanData {
    }

    [Implement(typeof(IReadOnlyData<>))]
    [Implement(typeof(IValueChangedData<>))]
    [Implement(typeof(IDataNotifier<>))]
    [Implement(typeof(IData<>))]
    public class KHBD_Data<T> : KHBD_PoolObject, IData<T> {

        public IData<T> AsReadWrite() {
            AssertDispose();
            return this;
        }

        //private KHBD_SortedCollection<IData<T>.ValueChangedDelegate> m_ValueChangedCollection;
        //public event IData<T>.ValueChangedDelegate ValueChanged {
        //    add => m_ValueChangedCollection.Add(value);
        //    remove => m_ValueChangedCollection.Remove(value);
        //}

        //private KHBD_SortedCollection<IChangable.ChangedDelegate> m_ChangedCollection;
        //public event IChangable.ChangedDelegate Changed {
        //    add => m_ChangedCollection.Add(value);
        //    remove => m_ChangedCollection.Remove(value);
        //}

        public event IData<T>.ValueChangedDelegate ValueChanged;
        public event IChangable.ChangedDelegate Changed;

        public KHBD_Data() {
            m_Value = GetDefaultValue();
        }

#if UNITY_EDITOR
        private Guid m_GUID;
#endif

        protected override void Allocate() {
#if UNITY_EDITOR
            m_GUID = Guid.NewGuid();
#endif
            //m_ValueChangedCollection = KHBD_SortedCollection<IData<T>.ValueChangedDelegate>.Pool.Get();
            //m_ChangedCollection = KHBD_SortedCollection<IChangable.ChangedDelegate>.Pool.Get();
        }

        protected override void Release() {
            //KHBD_SortedCollection<IData<T>.ValueChangedDelegate>.Pool.Release(m_ValueChangedCollection);
            //m_ValueChangedCollection = null;
            //KHBD_SortedCollection<IChangable.ChangedDelegate>.Pool.Release(m_ChangedCollection);
            //m_ChangedCollection = null;
            ValueChanged = null;
            Changed = null;
            m_Value = GetDefaultValue();
        }

        protected virtual T GetDefaultValue() => default;

        private T m_Value;

        T IReadOnlyData<T>.Value => Value;

        T IData<T>.Value {
            get => Value;
            set => Value = value;
        }

        public virtual T Value {
            get => m_Value;
            set => SetValue(value);
        }

        protected void SetValue(T value) {
            if (!value.Equals(m_Value)) {
                var old = m_Value;
                DoSetValue(value);
                Dirty(old, value);
            }
        }

        protected void Dirty(T old, T @new) {
            if (KHBD.Context.IsApplicationQuit) {
                return;
            }
            //foreach (var valueChanged in m_ValueChangedCollection) {
            //    valueChanged.Invoke(old, @new);
            //}
            ValueChanged?.Invoke(old, @new);
            NotifyChanged();
        }

        protected void NotifyChanged() {
            //foreach (var changed in m_ChangedCollection) {
            //    changed.Invoke();
            //}
            Changed?.Invoke();
        }

        void IDataNotifier<T>.NotifyChangedEvent() => NotifyChanged();

        void IDataNotifier<T>.NotifyValueChangedEvent() => Dirty(Value, Value);

        void IData<T>.SetValueWithOutEvent(T value) => SetValueWithOutEvent(value);

        private void SetValueWithOutEvent(T value) {
            if (!value.Equals(m_Value)) {
                DoSetValue(value);
            }
        }

        protected virtual void DoSetValue(T value) => m_Value = value;

        public override string ToString() => Value.ToString();
    }

    [Implement(typeof(IReadOnlyStringData))]
    [Implement(typeof(IValueChangedStringData))]
    [Implement(typeof(IStringDataNotifier))]
    [Implement(typeof(IStringData))]
    [Implement(typeof(IReadOnlyData<string>))]
    [Implement(typeof(IValueChangedData<string>))]
    [Implement(typeof(IDataNotifier<string>))]
    [Implement(typeof(IData<string>))]
    internal sealed class KHBD_StringData : KHBD_PoolObject, IStringData {

        private KHBD_SortedCollection<IData<string>.ValueChangedDelegate> m_ValueChangedCollection;
        public event IData<string>.ValueChangedDelegate ValueChanged {
            add => m_ValueChangedCollection.Add(value);
            remove => m_ValueChangedCollection.Remove(value);
        }

        private KHBD_SortedCollection<IChangable.ChangedDelegate> m_ChangedCollection;
        public event IChangable.ChangedDelegate Changed {
            add => m_ChangedCollection.Add(value);
            remove => m_ChangedCollection.Remove(value);
        }

        protected override void Allocate() {
            m_ValueChangedCollection = KHBD_SortedCollection<IData<string>.ValueChangedDelegate>.Pool.Get();
            m_ChangedCollection = KHBD_SortedCollection<IChangable.ChangedDelegate>.Pool.Get();
        }

        protected override void Release() {
            KHBD_SortedCollection<IChangable.ChangedDelegate>.Pool.Release(m_ChangedCollection);
            m_ChangedCollection = null;
            KHBD_SortedCollection<IData<string>.ValueChangedDelegate>.Pool.Release(m_ValueChangedCollection);
            m_ValueChangedCollection = null;
            m_Value = null;
        }

        private string m_Value;

        public string Value {
            get => m_Value;
            set => SetValue(value);
        }

        private void SetValue(string value) {
            if (!value.Equals(m_Value)) {
                var old = m_Value;
                DoSetValue(value);
                Dirty(old, value);
            }
        }

        private void Dirty(string old, string @new) {
            if (KHBD.Context.IsApplicationQuit) {
                return;
            }
            foreach (var valueChanged in m_ValueChangedCollection) {
                valueChanged.Invoke(old, @new);
            }
            NotifyChanged();
        }

        private void NotifyChanged() {
            foreach (var changed in m_ChangedCollection) {
                changed.Invoke();
            }
        }

        void IDataNotifier<string>.NotifyChangedEvent() => NotifyChanged();

        void IDataNotifier<string>.NotifyValueChangedEvent() => Dirty(Value, Value);

        void IData<string>.SetValueWithOutEvent(string value) => SetValueWithOutEvent(value);

        private void SetValueWithOutEvent(string value) {
            if (!value.Equals(m_Value)) {
                DoSetValue(value);
            }
        }

        private void DoSetValue(string value) => m_Value = value;

        public IData<string> AsReadWrite() => this;

        public override string ToString() => Value.ToString();
    }
}